import { useState, useEffect } from 'react';

/**
 * 基于发布订阅 + React Hooks 创建轻量级全局状态管理方法
 *
 * 特点：
 *      1. 状态全局化
 *      2. 定点更新，只有用到的组件才会被通知更新
 *      3. 轻量级，不需要 Provider Wrap ，不需要 observer；仅仅只是一个 hook 函数
 *      4. 组件卸载后自动取消订阅
 *      5. weak模式，createGlobalValue 创建出来的值是存在闭包内的，启用该模式后，当没有组件使用该值时，该值将会被回收。默认是启用的（通常情况下建议启用，防止内存泄漏）
 *      6. 通过 createGlobalValue 创建的 hooks 使用方法和 useState 完全一致
 *
 * 使用方法：
 *      const useUser = createGlobalValue({ name: '仙女' });
 *      const useNumber = createGlobalValue(0);
 *      const useList = createGlobalValue(['hello', 'world]);
 *
 * 使用范例：文件末尾
 *
 * @param initialValue 初始化value，可以传入初始化方法返回值就是初始化的值
 * @param weak 类似weakMap的作用
 */

type Handler<Value> = (value: Value) => void;

function createEventEmitter<Value>() {
    const eventEmitter = {
        pool: [] as Array<Handler<Value>>,
        listen: (handler: Handler<Value>) => eventEmitter.pool.push(handler),
        unlisten: (handler: Handler<Value>) => (eventEmitter.pool = eventEmitter.pool.filter(h => h !== handler)),
        emit: (value: Value) => eventEmitter.pool.forEach(handler => handler(value))
    };

    return eventEmitter;
}

type InitialStatus = 'created' | 'mounted' | 'sleeped' | 'reseted';

function createGlobalValue<
    Value extends (emit: ReturnType<typeof createEventEmitter>['emit'], status: InitialStatus) => any
>(
    initialValue: Value,
    weak?: boolean
): {
    (): [ReturnType<Value>, React.Dispatch<React.SetStateAction<ReturnType<Value>>>];
    reset(): ReturnType<Value>;
    getValue: () => Readonly<ReturnType<Value>>;
};

function createGlobalValue<Value = any>(
    initialValue: Value,
    weak?: boolean
): {
    (): [Value, React.Dispatch<React.SetStateAction<Value>>];
    reset(): Value;
    getValue: () => Readonly<Value>;
};

function createGlobalValue<Value = any>(initialValue: Value, weak: boolean = true) {
    let sleep = true;
    const eventEmitter = createEventEmitter<Value>();
    const getValue = (status: InitialStatus): Value =>
        typeof initialValue === 'function' ? initialValue(eventEmitter.emit, status) : initialValue;
    let _value: Value = getValue('created');

    function useGlobalValue() {
        const [value, setValue] = useState(
            sleep
                ? () => {
                      sleep = false;

                      return (_value = getValue('mounted'));
                  }
                : _value
        );

        useEffect(() => {
            const handler: Handler<Value> = value => {
                _value = value;

                setValue(value);
            };

            eventEmitter.listen(handler);

            return () => {
                const restHandlers = eventEmitter.unlisten(handler);

                if (weak && restHandlers.length === 0) {
                    sleep = true;
                    _value = getValue('sleeped');
                }

                return;
            };
        }, []);

        return [value, eventEmitter.emit] as [typeof value, typeof setValue];
    }

    useGlobalValue.reset = () => eventEmitter.emit(getValue('reseted'));
    useGlobalValue.getValue = () => Object.freeze(_value);

    return useGlobalValue;
}

export default createGlobalValue;

// 使用示例 1
// const useUser = createGlobalValue({ name: '仙女' });

// const A = () => {
//     const [user] = useUser();

//     return <div>{user.name}</div>;
// };

// const B = () => {
//     const [user, setUser] = useUser();

//     return (
//         <div>
//             {user.name}
//             <input value={user.name} onChange={v => setUser({ name: v.target.value })} />
//         </div>
//     );
// };

// 使用示例 2 -- “广告投放”
// 假设A组件是一个路由Layout，B组件是A的子路由，那么B组件想要给A组件添加内容，可以这样做
// const useADSpace = createGlobalValue(() => undefined as { title?: string; content?: ReactNode; } | undefined);

// const A = () => {
//     const [AD] = useADSpace();

//     return (
//         <div>
//             <div>
//                 <h1>{AD?.title}</h1>
//                 <div>{AD?.content}</div>
//             </div>
//             <B/>
//         </div>
//     )
// };

// const B = () => {
//     const [, setADSpace] = useADSpace();

//     useEffect(() => {
//         setADSpace({
//             title: 'esop',
//             content: <div>esop fed 666</div>,
//         })
//     }, [])

//     return (
//         <div>
//             {/** ... */}
//         </div>
//     );
// };

// 使用3: 封装自更新状态

// let resizeHandler: (() => void) | null = null;

// export const useWindowSize = createGlobalValue((emit, status) => {
//     if (status === 'mounted') {
//         console.info('%cmounted', 'color: green');

//         resizeHandler = _.debounce(() => {
//             emit({
//                 width: window.innerWidth,
//                 height: window.innerHeight
//             });
//         });

//         window.addEventListener('resize', resizeHandler);

//         return {
//             width: window.innerWidth,
//             height: window.innerHeight
//         };
//     }

//     if (status === 'sleeped') {
//         window.removeEventListener('resize', resizeHandler!);

//         resizeHandler = null;

//         console.info('%csleeped', 'color: red');
//     }

//     return null;
// });
